/*
 * $Id: InterfaceMapBuilder.java 1285 2007-02-08 14:19:09Z kofron $
 *
 * Behavior Protocols extensions for static and runtime checking
 * developed for the Julia implementation of Fractal.
 *
 * Copyright (C) 2006
 *    Formal Methods In Software Engineering Group
 *    Institute of Computer Science
 *    Academy of Sciences of the Czech Republic
 *
 * Copyright (C) 2006 France Telecom
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 *
 * Contact: ft@nenya.ms.mff.cuni.cz
 * Authors: Jiri Adamek <adamek@nenya.ms.mff.cuni.cz>
 *
 */

package org.objectweb.fractal.bpc.staticchecker.preprocessor;

import java.util.Map;
import java.util.Set;
import java.util.HashSet;

import org.objectweb.fractal.bpc.staticchecker.Binding;
import org.objectweb.fractal.bpc.staticchecker.FrameInstance;
import org.objectweb.fractal.bpc.staticchecker.InterfaceInstance;

/**
 * Creates the maps representing bindings and also the sets representing
 * unbound interfaces. 
 */
public class InterfaceMapBuilder {
	
	private static FrameInstance getFrameInstance(
			FrameInstance frameInstances[], String frameInstanceName) {
		for (int i = 0; i < frameInstances.length; i++)
			if (frameInstances[i].frameName.equals(frameInstanceName))
				return frameInstances[i];
		return null;
	}
	
	public static void updateMap(Map map, InterfaceInstance key, InterfaceInstance value) {
		Set set = (Set) map.get(key);
		if (set == null)
			set = new HashSet();
		set.add(value);
		map.put(key, set);
	}
	
	private static void buildMaps(Map interfaceMap, Map reverseInterfaceMap,
			FrameInstance superComponent, FrameInstance subcomponents[],
			Binding bindings[]) {

		for (int i = 0; i < bindings.length; i++) {
			
			FrameInstance fromFrame = bindings[i].frameInstance1 == null ? superComponent
					: getFrameInstance(subcomponents, bindings[i].frameInstance1);
			/* TODO add assert(fromFrame != null) */
			
			FrameInstance toFrame = bindings[i].frameInstance2 == null ? superComponent
					: getFrameInstance(subcomponents, bindings[i].frameInstance2);
			/* TODO add assert(toFrame != null) */
			
			InterfaceInstance fromInterface = fromFrame.getInterfaceForName(bindings[i].interfaceInstance1);
			/* TODO add assert(fromInterface != null) */
			
			InterfaceInstance toInterface = toFrame.getInterfaceForName(bindings[i].interfaceInstance2);
			/* TODO add assert(toInterface != null) */

			/* update interfaceMap */
			updateMap(interfaceMap, fromInterface, toInterface);
			
			/* update reverseInterfaceMap */
			updateMap(reverseInterfaceMap, toInterface, fromInterface);
			
		}
	}
	
	private static void identifyUnboundInFrame(Set unboundProvidedInterfaces,
			Set unboundRequiredInterfaces, FrameInstance frame,
			Map interfaceMap, Map reverseInterfaceMap) {
		
		for (int i = 0; i < frame.interfaces.length; i++) {
			InterfaceInstance inInst = frame.interfaces[i];
			Set set;
			if (Transformation.isTo(inInst)) {
				set = (Set) reverseInterfaceMap.get(inInst);
				if (set == null) {
					unboundProvidedInterfaces.add(inInst);
					reverseInterfaceMap.put(inInst, new HashSet());
				}
			} else {
				set = (Set) interfaceMap.get(inInst);
				if (set == null) {
					unboundRequiredInterfaces.add(inInst);
					interfaceMap.put(inInst, new HashSet());
					
				}
			}
		}
	}
	
	/* 
	 * It constructs the sets of unbound provided/required interfaces AND add empty sets
	 * (instead of null references) into the interface maps for the unbound interfaces.
	 */
	
	private static void identifyUnboundInterfaces(Set unboundProvidedInterfaces,
			Set unboundRequiredInterfaces, FrameInstance superComponent,
			FrameInstance[] subcomponents, Map interfaceMap,
			Map reverseInterfaceMap) {
		
		/* identify unbound interfaces in the frame of supercomponent */
		identifyUnboundInFrame(unboundProvidedInterfaces,
				unboundRequiredInterfaces, superComponent, interfaceMap,
				reverseInterfaceMap);

		/* identify unbound interfaces in the frames of subcomponents */
		for (int i = 0; i < subcomponents.length; i++) {
			identifyUnboundInFrame(unboundProvidedInterfaces,
					unboundRequiredInterfaces, subcomponents[i], interfaceMap,
					reverseInterfaceMap);
		}
	}
	
	public static void build(Map interfaceMap, Map reverseInterfaceMap,
			Set unboundProvidedInterfaces, Set unboundRequiredInterfaces,
			FrameInstance superComponent, FrameInstance subcomponents[],
			Binding bindings[]) {

		buildMaps(interfaceMap, reverseInterfaceMap, superComponent,
				subcomponents, bindings);

		identifyUnboundInterfaces(unboundProvidedInterfaces,
				unboundRequiredInterfaces, superComponent, subcomponents,
				interfaceMap, reverseInterfaceMap);

	}
}
